package org.qdog.pk.pkscrape.controller;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.qdog.pk.pkscrape.dao.Dao;
import org.qdog.pk.pkscrape.entity.Entity;
import org.qdog.pk.pkscrape.entity.Relationship;
import org.qdog.pk.pkscrape.util.RelationshipMap;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * @since 08/2011
 */
@Component
public class RelationshipMgr {
  private static final Logger LOG = Logger.getLogger(RelationshipMgr.class);
  private Dao dao;
  private EntityMgr entityMgr;

  /**
   * Returns all of the relationships for an entity keyed by the target entity id.
   *
   * @param entity the entity for which the relationships should be loaded
   * @return Map of the relationships
   */
  public RelationshipMap loadRelationshipMap(Entity entity) {
    Collection<Relationship> relationships = dao.loadActiveRelationshipsForEntity(entity);
    return new RelationshipMap(entity, relationships);
  }

  /**
   * Updates the relationships of a given type.  Current relationships to the target entities are ignored.  Target
   * entities without a current relationship will cause a new relationship to be added.  Any current relationship
   * to target entities not found in the list of target entities will be terminated.
   *
   * @param relationshipClass identifies the type of relationships to update
   * @param targetEntities    the target entity with which the relationship applies
   * @param relationshipMap   current relationships for the source entity
   */
  public void updateRelationships(Class<? extends Relationship> relationshipClass, Collection<String> targetEntities, RelationshipMap relationshipMap) {
    Collection<String> currentTargetEntityIds = relationshipMap.getTargetEntityIdsByRelationshipType(relationshipClass);
    Map<String, Entity> newTargetEntities = new HashMap<String, Entity>(targetEntities.size());
    for (String entityId : targetEntities) {
      newTargetEntities.put(entityId, null);
    }

    // remove entities from both collections where there is overlap.  These are existing current relationships.
    Collection<String> relationshipsToKeep = new ArrayList<String>(currentTargetEntityIds.size());
    for (String id : currentTargetEntityIds) {
      if (newTargetEntities.containsKey(id)) {
        relationshipsToKeep.add(id);
        newTargetEntities.remove(id);
      }
    }
    currentTargetEntityIds.removeAll(relationshipsToKeep);

    // load or create entities for the new relationships
    if (newTargetEntities.size() > 0) {
      LOG.info("Adding " + newTargetEntities.size() + " new "+relationshipClass.getSimpleName()+" relationships");

      for (String entityId : newTargetEntities.keySet()) {
        Entity entity = entityMgr.createOrLoadEntityById(entityId);
        newTargetEntities.put(entityId, entity);
      }


      // add the new relationships
      for (Entity targetEntity : newTargetEntities.values()) {
        Relationship r = relationshipMap.addRelationship(relationshipClass, targetEntity);
        dao.persist(r);
      }
    }

    // terminate the old relationships
    if (currentTargetEntityIds.size() > 0) {
      LOG.info("Terminating " + currentTargetEntityIds.size() + " "+relationshipClass.getSimpleName()+" relationships");
      for (String id : currentTargetEntityIds) {
        Relationship r = relationshipMap.getRelationshipByTargetEntity(relationshipClass, id);
        r.setEnd(new Date());
        dao.persist(r);
        relationshipMap.removeRelationshipByTargetEntity(relationshipClass, id);
      }
    }
  }

  /**
   * @see {@link #updateRelationships(Class, java.util.Collection, org.qdog.pk.pkscrape.util.RelationshipMap)}
   */
  public void updateRelationships(Class<? extends Relationship> relationshipClass, String targetEntityId, RelationshipMap relationshipMap) {
    Collection<String> entities = new ArrayList<String>(1);
    if (StringUtils.isNotBlank(targetEntityId)) {
      entities.add(targetEntityId);
    }
    updateRelationships(relationshipClass, entities, relationshipMap);
  }

  @Resource(name = "dao")
  public void setDao(Dao dao) {
    this.dao = dao;
  }

  @Resource(name = "entityMgr")
  public void setEntityMgr(final EntityMgr entityMgr) {
    this.entityMgr = entityMgr;
  }
}
